﻿using Microsoft.EntityFrameworkCore;
using Nantianmen.Domain.Models;
using Nantianmen.Domain.Repositories;
using Nantianmen.Shared;
using System.Net;

namespace Nantianmen.EntityFrameworkCore
{
    public class DestinationStore : DestinationStoreBase
    {
        protected DbContext DbContext { get; set; }

        protected virtual DbSet<Destination> DestinationModelSet => DbContext.Set<Destination>();

        public DestinationStore(DbContext dbContext)
        {
            this.DbContext = dbContext;
        }

        public override async Task<Destination?> FindByIdAsync(string address, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();

            return await DestinationModelSet.FindAsync(address, cancellationToken);
        }

        public override async Task InsertAsync(Destination model, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();

            await this.DestinationModelSet.AddAsync(model, cancellationToken);
            await SaveChangesAsync(cancellationToken);
        }

        public override async Task<List<Destination>> ListAllAsync(CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();

            return await DestinationModelSet.ToListAsync(cancellationToken);
        }

        public override async Task RemoveByAddressAsync(string address, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();

            var current = await FindByIdAsync(address, cancellationToken);
            if (current is null)
            {
                return;
            }
            DestinationModelSet.Remove(current);

            await SaveChangesAsync(cancellationToken);
        }

        public override async Task UpdateAsync(Destination model, CancellationToken cancellationToken)
        {
            DestinationModelSet.Attach(model);
            DestinationModelSet.Entry(model).State = EntityState.Modified;

            await SaveChangesAsync(cancellationToken);
        }

        protected override async Task SaveChangesAsync(CancellationToken cancellationToken)
        {
            await this.DbContext.SaveChangesAsync(cancellationToken);
        }

        public override async Task<List<Destination>> FindByAddressAsync(string address, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();

            var query = from destination in DestinationModelSet
                        where destination.Address.StartsWith(address)
                        select destination;

            return await query.ToListAsync(cancellationToken);
        }

        public override async Task RemoveByIdAsync(string id, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();

            var query = from destination in DestinationModelSet
                        where destination.Id == id
                        select destination;

            var current = await query.FirstOrDefaultAsync(cancellationToken);

            if (current is null)
            {
                return;
            }
            DestinationModelSet.Remove(current);

            await SaveChangesAsync(cancellationToken);
        }

        public override async Task<List<Destination>> LoadAsync(List<string> destinationIds, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            ThrowIfDisposed();

            if (destinationIds.IsNullOrEmpty())
            {
                return new List<Destination>();
            }

            var query = from destination in DestinationModelSet
                        where destinationIds.Contains(destination.Id)
                        select destination;

            return await query.ToListAsync(cancellationToken);
        }
    }
}
